// File:       sstream.h
// Version:    1.00
// Author:     (c) Miles Sabin, 1997
// Purpose:    approximation to ANSI C++ sstream header

// Change log:
//  05/04/97   v. 1.00

#include "sstream.h"

#include "algorithm.h"          // for max()


// Implementation of basic_stringbuf_char

basic_stringbuf_char::basic_stringbuf_char(ios_base::openmode which)
  : mode_(which),
    min_sequence_length_(0)
  {}

basic_stringbuf_char::basic_stringbuf_char(basic_string_char const& str, ios_base::openmode which)
  : mode_(which),
    buffer_(str),
    min_sequence_length_(0)
  { set_pointers(); }

basic_stringbuf_char::~basic_stringbuf_char()
  {}

basic_string_char basic_stringbuf_char::str() const
  {
    if(mode_ == ios_base::in)
      return string(eback(), egptr());
    else
      return string(pbase(), max(min_sequence_length_, pptr()-pbase()));
  }

void basic_stringbuf_char::str(basic_string_char const& s)
  {
    buffer_ = s;
    min_sequence_length_ = 0;
    set_pointers();
  }

int basic_stringbuf_char::underflow()
  { return traits::eof(); }

int basic_stringbuf_char::pbackfail(int c)
  {
    if(gptr() == eback())
      return traits::eof();

    if(c == traits::eof())
      c = traits::not_eof(c);
    else if(*(gptr()-1) != c)
    {
      if((mode_&ios_base::out) == 0)
        return traits::eof();
      *(gptr()-1) = c;
    }

    gbump(-1);

    return c;
  }

int basic_stringbuf_char::overflow(int c)
  {
    if((mode_&ios_base::out) == 0)
      return traits::eof();

    if(c == traits::eof())
      return traits::not_eof(c);

    int newoffi;
    int newoffo;

    if(gptr() != 0)
      newoffi = egptr()-eback();

    newoffo = epptr()-pbase();

    buffer_.append(1, c);
    buffer_.resize(buffer_.capacity());
    set_pointers();

    if(gptr() != 0)
      gbump(newoffi);

    pbump(newoffo);

    return sputc(c);
  }

int basic_stringbuf_char::seekoff(int off, ios_base::seekdir way, ios_base::openmode which)
  {
    int bits = which&(ios_base::in|ios_base::out);

    if(bits != ios_base::in &&
       bits != ios_base::out &&
       (bits != (ios_base::in|ios_base::out) || (way != ios_base::beg && way != ios_base::end)))
      return -1;

    bool seeki = ((which&ios_base::in) != 0);
    bool seeko = ((which&ios_base::out) != 0);

    if((seeki && eback() == 0) || (seeko && pbase() == 0))
      return -1;

    int newoff;

    switch(way)
    {
      case ios_base::beg:
        newoff = 0;
        break;

      case ios_base::cur:
        if(seeki)
          newoff = gptr()-eback();
        else
          newoff = pptr()-pbase();
        break;

      case ios_base::end:
        if(seeki)
          newoff = egptr()-eback();
        else
          newoff = epptr()-pbase();
        break;

      default:
        return -1;
    }

    if((newoff+off) < 0 || (egptr()-eback()) < (newoff+off))
      // no need to check newoffo as well
      return -1;

    if(seeki)
      setg(eback(), eback()+newoff, egptr());

    if(seeko)
    {
      setp(pbase(), epptr());
      pbump(newoff);
      min_sequence_length_ = max(min_sequence_length_, newoff);
    }

    return newoff;
  }

int basic_stringbuf_char::seekpos(int sp, ios_base::openmode which)
  { return seekoff(sp, ios_base::beg, which); }

void basic_stringbuf_char::set_pointers()
  {
    if((mode_&ios_base::in) != 0)
      setg(buffer_.begin(), buffer_.begin(), buffer_.end());

    if((mode_&ios_base::out) != 0)
      setp(buffer_.begin(), buffer_.end());
  }
